#![allow(unused)]
mod abort_on_drop;
mod iptables;
mod netem_cmd;
mod netem_server;
mod server;
mod util;
mod options;

use std::net::Ipv4Addr;

use anyhow;
use clap::Parser;
use netem_cmd::*;
use std::sync::Arc;
use tokio::{
    signal::unix::{signal, Signal, SignalKind},
    sync::mpsc::UnboundedSender,
};

use crate::abort_on_drop::AbortOnDropHandle;
use crate::options::{Opt, OPTS};
use crate::util::probe_interface_ip;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let (netem_server_handle, tx) = netem_server::start_server(OPTS.clone());

    let signal_handle: AbortOnDropHandle<()> = tokio::spawn(handle_signal(tx.clone())).into();

    let api_server_handle: AbortOnDropHandle<()> = tokio::spawn(server::run_server_loop(OPTS.clone(), tx.clone())).into();

    netem_server_handle.join().await;

    Ok(())
}

fn test_netem() -> anyhow::Result<()> {
    const ROOT_NET_IP: &'static str = "192.168.6.53";
    const USER_COMMAND: &'static str = "nc -l -p 5080";
    const TMP_DIR: &'static str = "/tmp";

    iptables::iptables_clear_nat()?;
    iptables::iptables_add_root_dnat(ROOT_NET_IP)?;

    // make_jitter_trace_file(500, "/home/kai/jitter.down")?;
    // make_burst_arrival_trace_file(2000, 30_000, "/home/kai/jitter.down")?;

    let empty = netem_cmd::build_full_command(ROOT_NET_IP, USER_COMMAND, TMP_DIR, vec![]);
    println!("empty=[{}]", empty);

    let simple_loss = build_full_command(
        ROOT_NET_IP,
        USER_COMMAND,
        TMP_DIR,
        vec![LossConfig::Stable(0.2).into()],
    );
    println!("simple_loss=[{}]", simple_loss);

    let simple_poisson = build_full_command(
        ROOT_NET_IP,
        USER_COMMAND,
        TMP_DIR,
        vec![LossConfig::Poisson(0.2).into()],
    );
    println!("simple_loss=[{}]", simple_poisson);

    let simple_delay = build_full_command(
        ROOT_NET_IP,
        USER_COMMAND,
        TMP_DIR,
        vec![DelayConfig::Value(100).into()],
    );
    println!("simple_delay=[{}]", simple_delay);

    let simple_jitter = build_full_command(
        ROOT_NET_IP,
        USER_COMMAND,
        TMP_DIR,
        vec![JitterConfig::Stable(50).into()],
    );
    println!("simple_jitter=[{}]", simple_jitter);

    let cfgs: Vec<NetemConfig> = vec![
        DelayConfig::Value(100).into(),
        LossConfig::Stable(0.2).into(),
        JitterConfig::Stable(50).into(),
    ];

    let complex = build_full_command(ROOT_NET_IP, USER_COMMAND, TMP_DIR, cfgs);
    println!("complex=[\n{}\n]", complex);

    Ok(())
}

async fn handle_signal(tx: UnboundedSender<netem_server::CtrlMsg>) {
    let mut sigint = signal(SignalKind::interrupt()).unwrap();
    let mut sigterm = signal(SignalKind::terminate()).unwrap();
    let mut sigquit = signal(SignalKind::quit()).unwrap();
    loop {
        tokio::select! {
            _ = sigint.recv() => {
                println!("recv SIGINT");
                let _ = tx.send(netem_server::CtrlMsg::Quit);
            },
            _ = sigterm.recv() => {
                println!("recv SIGTERM");
                let _ = tx.send(netem_server::CtrlMsg::Quit);
            },
            _ = sigquit.recv() => {
                println!("recv SIGQUIT");
                let _ = tx.send(netem_server::CtrlMsg::Quit);
            },
        }
    }
}
